﻿using NUnit.Framework;
using HappyTemplate;

namespace HappyTemplate.Tests
{
	[TestFixture]
	public class ParserTests : TestFixtureHelper
	{
		[Test]
		public void ParseSingleParameter()
		{
			Template template = ParseTemplate("aparameter", "irrelevent");
			Assert.AreEqual(1, template.Parameters.Length);
			Assert.AreEqual("aparameter", template.Parameters[0].Name);
		}

		[Test]
		public void ParseMultipleParameters()
		{
			Template template = ParseTemplate("p1, p2,p3", "irrelevent");
			Assert.AreEqual(3, template.Parameters.Length);
			Assert.AreEqual("p1", template.Parameters[0].Name);
			Assert.AreEqual("p2", template.Parameters[1].Name);
			Assert.AreEqual("p3", template.Parameters[2].Name);
		}

		[Test]
		public void ParseLiteralExpansion()
		{
			Template template = ParseTemplate("<$\"test\"$>");
			Assert.AreEqual(1, template.NonTerminals.Length);
			LiteralExpansion<string> stringExpansion = (LiteralExpansion<string>)template.NonTerminals[0];
			Assert.AreEqual("test", stringExpansion.Value);
		}

		[Test]
		public void ParseVariableExpansion()
		{
			Template template = ParseTemplate("<$test$>");
			Assert.AreEqual(1, template.NonTerminals.Length);
			Assert.IsInstanceOfType(typeof(VariableExpansion), template.NonTerminals[0]);
			VariableExpansion expansion = (VariableExpansion)template.NonTerminals[0];
			Assert.AreEqual("test", expansion.Identifier.Text);
		}


		[Test]
		public void ParseMutlipartIdentifierExpansion()
		{
			Template template = ParseTemplate("<$test1   .   test2  \t .  test3$>");
			Assert.AreEqual(1, template.NonTerminals.Length);
			Assert.IsInstanceOfType(typeof(VariableExpansion), template.NonTerminals[0]);
			VariableExpansion expansion = (VariableExpansion)template.NonTerminals[0];

			Assert.AreEqual("test1.test2.test3", expansion.Identifier.Text);

			Assert.AreEqual("test1", expansion.Identifier.Parts[0]);
			Assert.AreEqual("test2", expansion.Identifier.Parts[1]);
			Assert.AreEqual("test3", expansion.Identifier.Parts[2]);

		}

		[Test]
		public void ParseTemplateExpansion()
		{
			Template template = ParseTemplate("<$template()$>");
			Assert.AreEqual(1, template.NonTerminals.Length);
			TemplateExpansion expansion = (TemplateExpansion)template.NonTerminals[0];
			Assert.AreEqual(0, expansion.Arguments.Length);
		}
		[Test]
		public void ParseTemplateExpansionWithSingleArgument()
		{
			Template template = ParseTemplate("<$template(arg1)$>");
			Assert.AreEqual(1, template.NonTerminals.Length);
			TemplateExpansion expansion = (TemplateExpansion)template.NonTerminals[0];
			Assert.AreEqual(1, expansion.Arguments.Length);
			NamedExpansionBase arg1 = (NamedExpansionBase)expansion.Arguments[0];
			Assert.AreEqual("arg1", arg1.Identifier.Text);
		}

		[Test]
		public void ParseTemplateExpansionWithMultipleArguments()
		{
			Template template = ParseTemplate("<$template(arg1, arg2,arg3)$>");
			Assert.AreEqual(1, template.NonTerminals.Length);
			Assert.IsInstanceOfType(typeof(TemplateExpansion), template.NonTerminals[0]);
			TemplateExpansion expansion = (TemplateExpansion)template.NonTerminals[0];
			Assert.AreEqual(3, expansion.Arguments.Length);
			Assert.AreEqual("arg1", ((NamedExpansionBase)expansion.Arguments[0]).Identifier.Text);
			Assert.AreEqual("arg2", ((NamedExpansionBase)expansion.Arguments[1]).Identifier.Text);
			Assert.AreEqual("arg3", ((NamedExpansionBase)expansion.Arguments[2]).Identifier.Text);
		}

		[Test]
		public void ParseTemplateExpansionWithMultipleNestedArguments()
		{
			Template template = ParseTemplate("<$template(arg1(arga, argb), arg2(argc, argd, arge) , arg3)$>");
			Assert.AreEqual(1, template.NonTerminals.Length);
			Assert.IsInstanceOfType(typeof(TemplateExpansion), template.NonTerminals[0]);
			TemplateExpansion expansion = (TemplateExpansion)template.NonTerminals[0];
			Assert.AreEqual(3, expansion.Arguments.Length);

			Assert.AreEqual("arg1", ((NamedExpansionBase)expansion.Arguments[0]).Identifier.Text);
			Assert.AreEqual("arg2", ((NamedExpansionBase)expansion.Arguments[1]).Identifier.Text);
			Assert.AreEqual("arg3", ((NamedExpansionBase)expansion.Arguments[2]).Identifier.Text);

			expansion = (TemplateExpansion)((TemplateExpansion)template.NonTerminals[0]).Arguments[0];
			Assert.AreEqual(2, expansion.Arguments.Length);
			Assert.AreEqual("arga", ((NamedExpansionBase)expansion.Arguments[0]).Identifier.Text);
			Assert.AreEqual("argb", ((NamedExpansionBase)expansion.Arguments[1]).Identifier.Text);

			expansion = (TemplateExpansion)((TemplateExpansion)template.NonTerminals[0]).Arguments[1];
			Assert.AreEqual(3, expansion.Arguments.Length);
			Assert.AreEqual("argc", ((NamedExpansionBase)expansion.Arguments[0]).Identifier.Text);
			Assert.AreEqual("argd", ((NamedExpansionBase)expansion.Arguments[1]).Identifier.Text);
			Assert.AreEqual("arge", ((NamedExpansionBase)expansion.Arguments[2]).Identifier.Text);
		}


		[Test]
		public void ParseTemplateExpansionWithMultipleMixedNestedArguments()
		{
			Template template = ParseTemplate("<$template(arg1(arga, \"argb\"), arg2(\"argc\", argd, arge), arg3)$>");
			Assert.AreEqual(1, template.NonTerminals.Length);
			TemplateExpansion expansion = (TemplateExpansion)template.NonTerminals[0];
			Assert.AreEqual(3, expansion.Arguments.Length);
			Assert.AreEqual("arg1", ((NamedExpansionBase)expansion.Arguments[0]).Identifier.Text);
			Assert.AreEqual("arg2", ((NamedExpansionBase)expansion.Arguments[1]).Identifier.Text);
			Assert.AreEqual("arg3", ((NamedExpansionBase)expansion.Arguments[2]).Identifier.Text);

			expansion = (TemplateExpansion)((TemplateExpansion)template.NonTerminals[0]).Arguments[0];
			Assert.AreEqual(2, expansion.Arguments.Length);
			Assert.AreEqual("arga", ((NamedExpansionBase)expansion.Arguments[0]).Identifier.Text);
			Assert.AreEqual("argb", ((LiteralExpansion<string>)expansion.Arguments[1]).Value);

			expansion = (TemplateExpansion)((TemplateExpansion)template.NonTerminals[0]).Arguments[1];
			Assert.AreEqual(3, expansion.Arguments.Length);
			Assert.AreEqual("argc", ((LiteralExpansion<string>)expansion.Arguments[0]).Value);
			Assert.AreEqual("argd", ((NamedExpansionBase)expansion.Arguments[1]).Identifier.Text);
			Assert.AreEqual("arge", ((NamedExpansionBase)expansion.Arguments[2]).Identifier.Text);
		}

		[Test]
		public void ParseLookup()
		{
			TemplateSet ts = ParseTemplateSet(@"
lookup someLookup
{
	""inValue1"" = ""outValue1"",
	""inValue2"" = ""outValue2"",	
	""inValue3"" = ""outValue3"",
	default = ""defaultValue""
}
");
			Lookup lookup = ts.Context.TemplateSet.GetLookup(new Identifier(new SourceLocation(1, 1, "test input"),
				"someLookup", new[] { "someLookup" }));

			string value;

			Assert.IsTrue(lookup.TryGetValue("inValue1", out value));
			Assert.AreEqual("outValue1", value);

			Assert.IsTrue(lookup.TryGetValue("inValue2", out value));
			Assert.AreEqual("outValue2", value);

			Assert.IsTrue(lookup.TryGetValue("inValue3", out value));
			Assert.AreEqual("outValue3", value);

			Assert.AreEqual("defaultValue", lookup.DefaultValue);
		}

		[Test]
		public void ParseLookupExpansion()
		{
			Template t = ParseTemplate("<$testLookup[\"someValue\"]$>");
			Assert.AreEqual(1, t.NonTerminals.Length);

			LookupExpansion le = (LookupExpansion)t.NonTerminals[0];
			LiteralExpansion<string> inputStringExpansion = (LiteralExpansion<string>)le.InputExpansion;
			Assert.AreEqual("someValue", inputStringExpansion.Value);
		}

		[Test]
		public void ParseForEach()
		{
			Template t = ParseTemplate("<$foreach(n in x) n next$>");
			ForEachStatement fes = (ForEachStatement)t.NonTerminals[0];
			VariableExpansion enumerable = (VariableExpansion)fes.Enumerable;
			Assert.AreEqual("x", enumerable.Identifier.Text);
			Assert.AreEqual("n", fes.LoopVar.Text);
			Assert.AreEqual(1, fes.NonTerminals.Length);
			Assert.AreEqual("n", ((VariableExpansion)fes.NonTerminals[0]).Identifier.Text);
		}

		[Test]
		public void ParseForEachWithBetween()
		{
			Template t = ParseTemplate("<$foreach(n in x between \",\") n next$>");
			ForEachStatement fes = (ForEachStatement)t.NonTerminals[0];
			VariableExpansion enumerable = (VariableExpansion)fes.Enumerable;
			Assert.AreEqual("x", enumerable.Identifier.Text);
			Assert.AreEqual("n", fes.LoopVar.Text);
			Assert.AreEqual(",", ((LiteralExpansion<string>)fes.Between).Value);
			Assert.AreEqual(1, fes.NonTerminals.Length);
			Assert.AreEqual("n", ((VariableExpansion)fes.NonTerminals[0]).Identifier.Text);
		}

		[Test]
		public void	ParseBuiltInFunctionCall()
		{
			Template t = ParseTemplate(@"<$formatNumber(""fmt"", ""valueToFormat"")$>");
			BuiltInFunctionExpansion bife = (BuiltInFunctionExpansion)t.NonTerminals[0];
			Assert.AreEqual("formatNumber", bife.Identifier.Text);
		}

		//TODO:  ??
		//ParseSet()
		//Parse others?
	}
}
